package cn.tedu.ticket;/**
 * @Author 金帮嘉诚 @Date 2021-06-23 星期三 14:37
 */
/*本类用于解决实现接口下的多线程售票案例的数据安全问题*/

/**
 * 知识点
 * StringBuffer java1.0 append有同步
 * StringBuilder java1.5 append无同步
 */
public class TestRunnableV2 {
    public static void main(String[] args) {
        /**创建实现了Runnable接口的对象--用Runnable对象创建Thread对象*/
        TicketRunnableV2_2 ticketRunnableV2 = new TicketRunnableV2_2();

        Thread t1 = new Thread(ticketRunnableV2);
        Thread t2 = new Thread(ticketRunnableV2);
        Thread t3 = new Thread(ticketRunnableV2);
        Thread t4 = new Thread(ticketRunnableV2);

        t1.start();
        t2.start();
        t3.start();
        t4.start();
    }
}

/**
 * --实现接口
 * --写run方法方法声明、创建成员变量ticketNumber
 * --while循环
 * --synchronized代码块
 * --售票if判断代码块
 * --跳出break时的if判断
 */

class TicketRunnableV2_2 implements Runnable {
    //用接口的方式，票数不用设置成静态
    int ticketNumber = 100;
    /*实现接口的方式，不限制锁对象的类型，只需要保持锁对象的唯一
     * 也就是提取到成员位置创建对象即可，因为target对象只创建一次*/
    Object object = new Object();

    /*如果一个方法中的所有带啊吗均需要被同步，那么可以用synchronized修饰
    用synchronized关键字修饰的方法被称为同步方法，不常用*/
    @Override
//    synchronized public void run(){
    public void run() {
        while (true) {

//            synchronized(TicketRunnableV2.class){
//            synchronized(object){
            /*注意：锁对象必须唯一，此种方式不正确
             * 原因：每个线程执行run()时都会new一个锁对象*/
//            synchronized(new Object())
            synchronized (this) {
                try {
                    Thread.sleep(10);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                if (ticketNumber > 0) {
                    System.out.println(Thread.currentThread().getName() + "=" + ticketNumber--);
                }
                if (ticketNumber < 0) break;
            }
        }
    }
}


